package com.gzz.orchid.common.boot.mybatis.interceptor;

import com.gzz.orchid.common.boot.mybatis.utils.MybatisUtils;
import com.gzz.orchid.common.boot.mybatis.dialect.DialectFactory;
import com.gzz.orchid.common.boot.mybatis.dialect.IDialect;
import com.gzz.orchid.common.core.model.Pagination;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;


/**
 * MyBatis 物理分页拦截器
 *
 * @author Gm
 */
@Slf4j
@Intercepts({
        @Signature(method = "prepare",type = StatementHandler.class, args = {Connection.class, Integer.class})
})
public class PaginationInterceptor implements Interceptor {
    private IDialect dialect;
    private String regexSplitPage;
    @Override
    public Object intercept(Invocation inv) throws Throwable {
        log.info("进入分页Interceptor。。{}。。。。。。。。",inv.getTarget().getClass().getName());
        // 1. 获取 StatementHandler 对象也就是执行语句, 默认是RoutingStatementHandler
        StatementHandler statementHandler = (StatementHandler) inv.getTarget();

        // 2. MetaObject 是 MyBatis 提供的一个反射帮助类，可以优雅访问对象的属性，这里是对 statementHandler 对象进行反射处理，
        //获取statementHandler包装类
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        //MetaObject metaObject = MetaObject.forObject(statementHandler,
        //         SystemMetaObject.DEFAULT_OBJECT_FACTORY,
        //         SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
        //         new DefaultReflectorFactory());

        // 获取查询接口映射的相关信息
        // 3. 通过 metaObject 反射获取 statementHandler 对象的成员变量 mappedStatement
        // MappedStatement ms = (MappedStatement) inv.getArgs()[0];
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        // mappedStatement 对象的 id 方法返回执行的 mapper 方法的全路径名
        String msId = mappedStatement.getId();
        SqlCommandType commandType = mappedStatement.getSqlCommandType();
        log.info("==>命令类型 SqlCommandType: {}", commandType);
        // 5. 获取包含原始 sql 语句的 BoundSql 对象
        BoundSql boundSql = statementHandler.getBoundSql();
        String originalSql  = boundSql.getSql();
        log.info("OriginalSql {}", originalSql);
        // 拦截以.ByPager结尾的请求。分页功能的统一实现
        if (msId.matches(this.regexSplitPage)) {

            // 获取方法的参数
            Object params = boundSql.getParameterObject();
            //log.info("Params {}", params);

            // 获取分页参数
            Pagination pager = extractPager(params);
            // 统计总记录
            Connection connection = (Connection) inv.getArgs()[0];
            int totalRecord = calcTotalRecords(connection, statementHandler, originalSql);
            pager.setTotal(totalRecord);
            // 重新构建为分页功能的SQL语句
            String sqlQuery = this.dialect.rebuildSql(originalSql, pager);
            log.info("重构分页后的SQL语句： {} ", sqlQuery);
            // 将构建完成的SQL语句赋值个体
            metaObject.setValue("delegate.boundSql.sql", sqlQuery);
            log.info("重构分页后的SQL语句： {} ", metaObject);
            // 采用物理分页后，就不需要mybatis的内存分页了，所以重置下面的两个参数
            metaObject.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
            metaObject.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);


            String modifySql  = boundSql.getSql();
            log.info("Modify SQL {}", modifySql);
            //ParameterMapping parameterMapping = new ParameterMapping.Builder().property("limit").mode(ParameterMode.IN).javaType(Integer.class).build();
                    //new ParameterMapping.Builder(mappedStatement.getConfiguration(), "limit", Integer.class).build();
//            // 改写 SQL2 为分页查询
//            String pageSql = originalSql + " LIMIT ? OFFSET ?";
//            Field field = BoundSql.class.getDeclaredField("sql");
//            field.setAccessible(true);
//            field.set(boundSql, pageSql);
            // 追加分页参数
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            boolean hasTenantId=false;
            for (ParameterMapping parameterMapping : parameterMappings) {
                //                if (parameterMapping.getProperty().equals(TENANT_ID)) {
                //                    hasTenantId=true;
                //                }
                log.info("Property::>> {}", parameterMapping);
            }
            metaObject.setValue("delegate.boundSql.parameterMappings", parameterMappings);

//            // 添加分页参数值
//            if (params instanceof MapperMethod.ParamMap<?> ) {
//                log.info("****分页参数： {}", params);
//                MapperMethod.ParamMap<Object> paramMap = (MapperMethod.ParamMap<Object>) params;
//                paramMap.put("limit", pager.getSize());
//                paramMap.put("offset", pager.getOffset());
//            }
        }else{
            log.info("****非分页的。。。。。。。。。。");
        }
        String formatterSql = MybatisUtils.resolveSql(mappedStatement.getConfiguration(), boundSql);
        // 当 SQL 执行超过我们设置的阈值，转为 WARN 级别
        log.info("\r\n方法名称:  {} \r\n执行SQL:  {} ", msId, formatterSql);
        // 将执行权交给下一个拦截器
        return inv.proceed();
    }

    // 获取代理对象
    @Override
    public Object plugin(Object target) {
        log.info("分页代理Plugin: {}", target.getClass().getName());
        //        if (target instanceof StatementHandler || target instanceof ResultSetHandler) {
        //            return Plugin.wrap(target, this);
        //        } else {
        //            return target;
        //        }
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties prop) {
        log.info("启用物理分页 Properties: {}", prop);
        this.dialect = DialectFactory.build(prop.getProperty("DIALECT", "mysql"));
        this.regexSplitPage = prop.getProperty("REGEX_SPLIT_PAGE", ".*ByPage$");
    }


    /**
     * 获取分页类
     *
     * @param parameterObject 对象
     * @return 分页对象
     */
    private Pagination extractPager(Object parameterObject) {
        if (parameterObject instanceof Pagination) {
            return (Pagination) parameterObject;
        } else if (parameterObject instanceof java.util.HashMap) {
            for (Object value : ((Map<?, ?>) parameterObject).values()) {
                if (value instanceof Pagination) {
                    return (Pagination) value;
                }
            }
        }
        return new Pagination();
    }

    /**
     * 统计总记录SQL
     */
    private int calcTotalRecords(Connection connection, StatementHandler statementHandler, String originalSql) throws SQLException {
        String countSql = "select count(1) as tot from(" + originalSql + ") as countTab";

        PreparedStatement countStmt = connection.prepareStatement(countSql);
        statementHandler
                .getParameterHandler()
                .setParameters(countStmt);
        ResultSet rs = countStmt.executeQuery();
        // 总记录数
        int totalRecord = 0;
        if (rs.next()) {
            totalRecord = rs.getInt("tot");
        }
        rs.close();
        countStmt.close();
        return totalRecord;
    }
}